from __future__ import annotations

"""
订阅管理路由（subscriptions）
---------------------------
职责：
- 普通用户：查询/新增/更新自己对策略的订阅；单个或一键暂停/恢复
- 管理员：查看所有用户的订阅总览

说明：
- upsert 兼容前端不传风控字段，后端填充默认值
- 响应中包含策略基础字段，方便前端直接展示
"""

from typing import List
from decimal import Decimal

from fastapi import APIRouter, Depends, HTTPException, Response, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from ..db import get_session
from ..config import get_settings
from ..deps import require_admin, require_user
from ..models import AuditLog, Script, ScriptSubscription, User
from ..schemas import (
    AdminSubscriptionEntry,
    SubscriptionRequest,
    SubscriptionResponse,
    SubscriptionUpdateRequest,
)
from ..services.utils import default_account_name

router = APIRouter(prefix="/subscriptions", tags=["subscriptions"])


@router.get(
    "",
    response_model=List[SubscriptionResponse],
    summary="我的订阅列表（普通用户）",
    description="列出当前登录用户订阅的策略及其分配、风控与账户信息。",
)
async def list_subscriptions(
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> List[SubscriptionResponse]:
    """列出当前用户的订阅。

    - 连接脚本表返回基础信息
    - 便于前端直接展示订阅与策略状态
    """
    stmt = (
        select(ScriptSubscription, Script)
        .join(Script, ScriptSubscription.script_id == Script.id)
        .where(
            ScriptSubscription.user_id == current_user.id,
            ScriptSubscription.hidden.is_(False),
            Script.hidden.is_(False),
        )
    )
    result = await session.execute(stmt)
    items = []
    for sub, script in result.all():
        items.append(
            SubscriptionResponse(
                script_id=script.id,
                script_name=script.name,
                symbol=script.symbol,
                market_type=script.market_type,
                enabled=sub.enabled,
                allocation=sub.allocation,
                max_notional=sub.max_notional,
                slippage_bps=sub.slippage_bps,
                status=script.status,
                connector_name=sub.connector_name,
                account_name=sub.account_name,
            )
        )
    return items


@router.post(
    "",
    response_model=SubscriptionResponse,
    status_code=status.HTTP_201_CREATED,
    summary="新增或更新订阅（普通用户）",
    description=(
        "为当前用户新增/更新指定脚本的订阅配置，需要提供交易所 connector 与账户名。\n\n"
        "注意：max_notional、slippage_bps 前端可不传——\n"
        "- 新建时：若未提供，将使用后端默认（MAX_NOTIONAL_DEFAULT、SLIPPAGE_BPS_LIMIT）；\n"
        "- 更新时：仅当传入非空值才会覆盖，未提供则保留原值。"
    ),
)
async def upsert_subscription(
    payload: SubscriptionRequest,
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> SubscriptionResponse:
    """新增或更新订阅。

    - 新建：填充默认 max_notional/slippage_bps
    - 更新：仅对传入字段进行覆盖
    """
    script_stmt = select(Script).where(
        Script.id == payload.script_id,
        Script.hidden.is_(False),
    )
    result = await session.execute(script_stmt)
    script = result.scalar_one_or_none()
    if not script:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Script not found")

    connector_name = (payload.connector_name or "").strip()
    if not connector_name:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="connector_name is required")
    account_name = (payload.account_name or "").strip() or default_account_name(current_user.id, current_user.email)

    sub_stmt = select(ScriptSubscription).where(
        ScriptSubscription.user_id == current_user.id,
        ScriptSubscription.script_id == script.id,
    )
    result = await session.execute(sub_stmt)
    sub = result.scalar_one_or_none()
    settings = get_settings()

    if sub is None:
        sub = ScriptSubscription(
            user_id=current_user.id,
            script_id=script.id,
            enabled=payload.enabled,
            allocation=payload.allocation,
            max_notional=(
                payload.max_notional
                if payload.max_notional is not None
                else Decimal(str(settings.max_notional_default))
            ),
            slippage_bps=(
                payload.slippage_bps
                if payload.slippage_bps is not None
                else settings.slippage_bps_limit
            ),
            connector_name=connector_name,
            account_name=account_name,
            hidden=False,
        )
        session.add(sub)
    else:
        if sub.hidden:
            sub.hidden = False
        sub.enabled = payload.enabled
        sub.allocation = payload.allocation
        if payload.max_notional is not None:
            sub.max_notional = payload.max_notional
        if payload.slippage_bps is not None:
            sub.slippage_bps = payload.slippage_bps
        sub.connector_name = connector_name
        sub.account_name = account_name

    audit_payload = payload.dict()
    audit_payload["connector_name"] = connector_name
    audit_payload["account_name"] = account_name

    session.add(
        AuditLog(
            actor_id=current_user.id,
            action="subscription.upsert",
            target=f"script:{script.id}",
            payload_json=audit_payload,
        )
    )
    await session.commit()
    return SubscriptionResponse(
        script_id=script.id,
        script_name=script.name,
        symbol=script.symbol,
        market_type=script.market_type,
        enabled=sub.enabled,
        allocation=sub.allocation,
        max_notional=sub.max_notional,
        slippage_bps=sub.slippage_bps,
        status=script.status,
        connector_name=sub.connector_name,
        account_name=sub.account_name,
    )


@router.patch(
    "/{script_id}",
    response_model=SubscriptionResponse,
    summary="更新我的订阅（部分更新）",
    description="按脚本ID更新当前用户的订阅，支持部分字段（enabled/allocation/max_notional/slippage_bps/connector_name/account_name）。",
)
async def update_subscription(
    script_id: int,
    payload: SubscriptionUpdateRequest,
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> SubscriptionResponse:
    """按脚本 ID 部分更新我的订阅。"""
    stmt = (
        select(ScriptSubscription, Script)
        .join(Script, ScriptSubscription.script_id == Script.id)
        .where(
            ScriptSubscription.user_id == current_user.id,
            ScriptSubscription.script_id == script_id,
            ScriptSubscription.hidden.is_(False),
            Script.hidden.is_(False),
        )
    )
    result = await session.execute(stmt)
    row = result.one_or_none()
    if not row:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Subscription not found")
    sub, script = row

    if payload.enabled is not None:
        sub.enabled = payload.enabled
    if payload.allocation is not None:
        sub.allocation = payload.allocation
    if payload.max_notional is not None:
        sub.max_notional = payload.max_notional
    if payload.slippage_bps is not None:
        sub.slippage_bps = payload.slippage_bps
    if payload.connector_name is not None:
        sub.connector_name = payload.connector_name.strip()
    if payload.account_name is not None:
        sub.account_name = payload.account_name.strip()

    await session.commit()
    return SubscriptionResponse(
        script_id=script.id,
        symbol=script.symbol,
        market_type=script.market_type,
        enabled=sub.enabled,
        allocation=sub.allocation,
        max_notional=sub.max_notional,
        slippage_bps=sub.slippage_bps,
        status=script.status,
        connector_name=sub.connector_name,
        account_name=sub.account_name,
    )


@router.post(
    "/{script_id}/pause",
    response_model=SubscriptionResponse,
    summary="暂停我的订阅",
)
async def pause_my_subscription(
    script_id: int,
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> SubscriptionResponse:
    """快捷暂停当前脚本的订阅。"""
    return await update_subscription(script_id, SubscriptionUpdateRequest(enabled=False), current_user, session)


@router.post(
    "/{script_id}/resume",
    response_model=SubscriptionResponse,
    summary="恢复我的订阅",
)
async def resume_my_subscription(
    script_id: int,
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> SubscriptionResponse:
    """快捷恢复当前脚本的订阅。"""
    return await update_subscription(script_id, SubscriptionUpdateRequest(enabled=True), current_user, session)


@router.delete(
    "/{script_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="退出我的订阅",
    description="彻底退出指定策略的订阅，软删除记录以防误下单。操作幂等，若已退出则直接返回 204。",
)
async def remove_subscription(
    script_id: int,
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> Response:
    stmt = select(ScriptSubscription).where(
        ScriptSubscription.user_id == current_user.id,
        ScriptSubscription.script_id == script_id,
    )
    result = await session.execute(stmt)
    sub = result.scalar_one_or_none()

    if sub is None or sub.hidden:
        return Response(status_code=status.HTTP_204_NO_CONTENT)

    sub.enabled = False
    sub.hidden = True
    await session.commit()
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.post(
    "/pause-all",
    response_model=int,
    summary="一键暂停我的所有订阅",
)
async def pause_all_subscriptions(
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> int:
    """一键暂停我所有订阅，返回受影响的订阅数。"""
    stmt = select(ScriptSubscription).where(
        ScriptSubscription.user_id == current_user.id,
        ScriptSubscription.hidden.is_(False),
    )
    result = await session.execute(stmt)
    subs = result.scalars().all()
    count = 0
    for sub in subs:
        if sub.enabled:
            sub.enabled = False
            count += 1
    await session.commit()
    return count


@router.post(
    "/resume-all",
    response_model=int,
    summary="一键恢复我的所有订阅",
)
async def resume_all_subscriptions(
    current_user=Depends(require_user),
    session: AsyncSession = Depends(get_session),
) -> int:
    """一键恢复我所有订阅，返回受影响的订阅数。"""
    stmt = select(ScriptSubscription).where(
        ScriptSubscription.user_id == current_user.id,
        ScriptSubscription.hidden.is_(False),
    )
    result = await session.execute(stmt)
    subs = result.scalars().all()
    count = 0
    for sub in subs:
        if not sub.enabled:
            sub.enabled = True
            count += 1
    await session.commit()
    return count


@router.get(
    "/admin/all",
    response_model=list[AdminSubscriptionEntry],
    summary="所有订阅总览（管理员）",
    description="管理员查看全量订阅及其用户、脚本与风控信息。",
)
async def admin_subscriptions(
    _: object = Depends(require_admin),
    session: AsyncSession = Depends(get_session),
) -> list[AdminSubscriptionEntry]:
    """管理员查看全量订阅条目。"""
    stmt = (
        select(ScriptSubscription, Script, User)
        .join(Script, ScriptSubscription.script_id == Script.id)
        .join(User, ScriptSubscription.user_id == User.id)
        .where(ScriptSubscription.hidden.is_(False))
    )
    result = await session.execute(stmt)
    items: list[AdminSubscriptionEntry] = []
    for sub, script, user in result.all():
        items.append(
            AdminSubscriptionEntry(
                user_id=user.id,
                user_email=user.email,
                script_id=script.id,
                script_name=script.name,
                symbol=script.symbol,
                market_type=script.market_type,
                enabled=sub.enabled,
                allocation=sub.allocation,
                max_notional=sub.max_notional,
                slippage_bps=sub.slippage_bps,
                status=script.status,
                connector_name=sub.connector_name,
                account_name=sub.account_name,
            )
        )
    return items
